package mx.com.crowdgine.crowds.samples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.media.opengl.GLAutoDrawable;

import org.w3c.dom.Node;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opencl.gl.CLGLBuffer;

import mx.com.crowdgine.assetloader.Asset;
import mx.com.crowdgine.crowds.CrowdSystem;
import mx.com.crowdgine.display.jogl.FrameBuffer;
import mx.com.crowdgine.display.jogl.shader.ShaderObject;
import mx.com.crowdgine.util.gpucompute.jocl.JoclGpuCompute;
import mx.com.crowdgine.util.xml.XMLReader;

public class StadiumCrowds implements CrowdSystem{
	
	private List<Asset> assets = new ArrayList<Asset>();
	private FrameBuffer frameBuffer = new FrameBuffer();
	private ShaderObject crowdPosShader;
	private ShaderObject particleShader;
	private Asset asset;
	
	//Kernel files
	private final String KERNEL_FILE_INIT = "StadiumInitKernel.cl";
	
	//Kernel names
	private final String KERNEL_INIT = "initCrowd";
	
	//Buffers to use
	public static final String AGENTS_NAME = "agents";
	
	private boolean verbose = false;
	private boolean initialized = false;
	protected JoclGpuCompute crowd;
	
	//Init variables for the crowd
	private int crowdSize = 0;
	
	public StadiumCrowds(Node crowds, boolean... verbose){
		//set debug property
		if(verbose.length > 0)
			this.verbose = verbose[0];
		
		LinkedList<HashMap<String, String>> crowdsInfo = XMLReader.getNodeInformation(crowds);
		
		//Init crowd size, default = 16 * 16
		String size = crowdsInfo.get(0).get(XMLReader.CROWDS_SIZE);
		if(size != null){
			crowdSize = Integer.parseInt(size);
			if(crowdSize < 16)
				crowdSize = 16;
		}else{
			crowdSize = 256;
		}
		
		//Get the tags inside the crowds node
		//HashMap<String, Node> crowdsHash = XMLReader.buildHashMap(crowds);
	}

	@Override
	public void initCrowd(Object drawable) {
		if(drawable instanceof GLAutoDrawable) {
			crowd  = new JoclGpuCompute((GLAutoDrawable)drawable, verbose);
			crowd.initCL(this.getClass().getResourceAsStream(KERNEL_FILE_INIT));
		
			//Initialize Buffers
			int size = Buffers.SIZEOF_FLOAT * 4 * crowdSize * crowdSize;
			crowd.createCLGLBuffer((GLAutoDrawable)drawable, AGENTS_NAME, size, CLGLBuffer.Mem.WRITE_ONLY);
		
			
			crowd.setKernelArg(KERNEL_INIT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_INIT, 1, crowdSize);
			crowd.setKernelArg(KERNEL_INIT, 2, 640);
			crowd.setKernelArg(KERNEL_INIT, 3, 400);
			crowd.setKernelArg(KERNEL_INIT, 4, 480);
		
			crowd.commandQueue.putAcquireGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.put2DRangeKernel(crowd.kernels.get(KERNEL_INIT), 0, 0, crowdSize, crowdSize, 0, 0)
								.putReleaseGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.finish();
			
			initialize(drawable);
			
			initialized = true;
		} else {
			throw new UnsupportedOperationException("StadiumCrowds: Drawable implementation " + drawable + " not currently supported.");
		}
		
	}

	@Override
	public void initCrowd(Object drawable, float[] roadMap, int roadMapWidth, int roadMapHeight, int scale) {	
	}
	
	public void initialize(Object drawable){
	}

	@Override
	public boolean isInitialized() {
		return initialized;
	}

	@Override
	public void updateCrowd(float dt) {
		update(dt);
	}
	
	public void update(float dt){
	}

	@Override
	public int getVertexObjectId(String buffer) {
		return crowd.getVertexObjectId(buffer);
	}

	@Override
	public int getCrowdSize() {
		return crowdSize * crowdSize;
	}

	public void addAsset(Asset asset) {
		this.assets.add(asset);
	}
	
	public List<Asset> getAssets() {
		return this.assets;
	}

	public FrameBuffer getFrameBuffer() {
		return frameBuffer;
	}

	public void setFrameBuffer(FrameBuffer frameBuffer) {
		this.frameBuffer = frameBuffer;
	}

	public ShaderObject getCrowdPosShader() {
		return crowdPosShader;
	}

	public void setCrowdPosShader(ShaderObject crowdPosShader) {
		this.crowdPosShader = crowdPosShader;
	}

	public ShaderObject getParticleShader() {
		return particleShader;
	}

	public void setParticleShader(ShaderObject particleShader) {
		this.particleShader = particleShader;
	}

}
